﻿#region License Information

/* •——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————•
   | Copyright Aptillon, Inc.                                                                                        |
   | This file is part of the CKS:API project - cksapi.codeplex.com                                                       |
   | Usage, modification and distribution subject to the license terms available here: http://cksapi.codeplex.com/license |
   |                                                                                                                      |
   | Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND,             |
   | either express or implied.                                                                                           |
   |                                                                                                                      |
   | THIS MESSAGE MAY NOT BE LEGALLY REMOVED                                                                              |
   •——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————• */

#endregion

using System;
using CKS.API.Exceptions;
using CKS.API.Logging;
using CKS.API.Utilities;
using Microsoft.SharePoint;

namespace CKS.API.Contracts
{
    /// <summary>
    /// Check whether the <see cref="ValidationCondition" /> is true, throwing a <see cref="CKSContractException" /> if it is false
    /// </summary>
    public class CodeContract
    {

        #region Fields

        private readonly ErrorDisplayType _errorDisplay;

        private readonly CKSLogger _logger;

        #endregion

        #region Constructors
        /// <summary>
        /// Create a new instance of the CodeContract class.  
        /// </summary>
        /// <param name="logger">The <see cref="CKSLogger"/> instance to record any contract violations</param>
        /// <param name="defaultErrorDisplay">The <see cref="ErrorDisplayType"/> that specifies how contract violations should be displayed to the user.</param>
        public CodeContract(CKSLogger logger, ErrorDisplayType defaultErrorDisplay)
        {
            _logger = logger;
            _errorDisplay = defaultErrorDisplay;
        }

        /// <summary>
        /// Create a new instance of the CodeContract class.  Any contract violations will be displayed on the Status Bar
        /// </summary>
        /// <param name="logger">The <see cref="CKSLogger"/> instance to record any contract violations</param>
        public CodeContract(CKSLogger logger)
        {
            _logger = logger;
            _errorDisplay = ErrorDisplayType.StatusBar;
        }

        /// <summary>
        /// Creates a new instance of the Code Contract Class.  Any contract violations will be thrown to the caller as CKSContractExceptions
        /// </summary>
        public CodeContract()
        {
            _errorDisplay = ErrorDisplayType.StatusBar;
        }

        #endregion

        #region Public Methods
        /// <summary>
        /// Verify that all passed objects are not null
        /// </summary>
        /// <param name="targets">The array of objects to check.</param>
        /// <returns></returns>
        public static ValidationCondition AllAreNotNull(params object[] targets)
        {
            ValidationCondition retVal = new ValidationCondition()
            {
                Result = true,
                ContractName = "AllAreNotNull",
                ContractTarget = "(Unknown)", //see GetContractTarget in CodeContractExtensions.cs
                TargetExpectedValue = "'None are null'"
            };
            int i = 0;
            foreach (object oneTarget in targets) // (int i = 0; i < target.Length; i++)
            {
                if (null == oneTarget)
                {
                    retVal.Result = false;
                    retVal.TargetActualValue = string.Format("'Item {0} is null'", i + 1);
                    break;
                }
                i++;

            }
            return retVal;
        }

        /// <summary>
        /// Check whether the specified <see cref="CKSContractException"/> is met.  If so, do nothing.  If not, throw and handle a <see cref="CKSContractException"/>
        /// </summary>
        /// <param name="conditionToCheck">The <see cref="CKSValidationCondition"/> that must be met to fulfill this contract</param>
        /// <exception cref="CKSContractException">The <see cref="CKSValidationCondition"/> is not met</exception>
        public void RequiresThat(ValidationCondition conditionToCheck)
        {
            try
            {
                if (!conditionToCheck.Result)
                {
                    throw new CKSContractException(conditionToCheck.ContractTarget,
                              conditionToCheck.ContractName,
                              conditionToCheck.TargetActualValue,
                              conditionToCheck.TargetExpectedValue,
                              true,
                              _logger);
                }
            }
            catch (CKSContractException ex)
            {
                if (Check.ThatIsNotNull(_logger))
                {
                    _logger.HandleException(new CKSExceptionDetails(ex, CKSLoggingCategory.Contracts)
                    {
                        Display = ErrorDisplayType.None,
                        ThrowingControl = null,
                        Severity = LoggingSeverity.Error
                    });
                }
                else
                {
                    throw;
                }

            }

        }

        public static ValidationCondition ValidateIsNotNull(object target)
        {
            object locker = new object();

            //TODO: Why is this being locked?
            lock (locker)
            {
                return new ValidationCondition
                {
                    Result = (null != target),
                    ContractName = "IsNotNull",
                    ContractTarget = CodeContractExtensions.GetContractTarget(),
                    TargetActualValue = null == target ? "null" : target.ToString(),
                    TargetExpectedValue = "not null"
                };
            }
        }

        public void RequiresSPContext()
        {
            CKSApiUtilities.EnsureSPContext();
            RequiresThat(ValidateIsNotNull(SPContext.Current));
        }

        public void RequiresSPContext(string webUrl)
        {
            CKSApiUtilities.EnsureSPContext(webUrl);
            RequiresThat(ValidateIsNotNull(SPContext.Current));
        }
        #endregion

    }
}
